--$Id: extend.lua 522875 2022-01-27 09:45:52Z lupulin $
--lua的扩展库
local string=string
local table=table
local math=math
local io=io
local pairs=pairs
local ipairs=ipairs
local tostring=tostring
local tonumber=tonumber

local string_gsub = string.gsub
local string_find = string.find
local string_sub = string.sub
local math_random = math.random
local table_remove = table.remove
local os_date = os.date
local os_time = os.time

local DAILY_HOUR = 5
local MaxDepth = 20


-------------------------------------------------------------------------------
--string库扩展
-------------------------------------------------------------------------------
function string.utf8len(txt)
	local _, len = string_gsub(txt, "[^\128-\193]", "")
	return len
end

-- utf8字符的字节数，注意：index必须指向字符的第一个字节才能正确返回字节数
function string.get_utf8_byte_count(str, index)
	local curByte = string.byte(str, index)
	local byteCount = 0
	if not curByte then return byteCount end

	if curByte > 0 and curByte <= 127 then
		byteCount = 1
	elseif curByte>=192 and curByte<=223 then
		byteCount = 2
	elseif curByte>=224 and curByte<=239 then
		byteCount = 3
	elseif curByte>=240 and curByte<=247 then
		byteCount = 4
	end

	return byteCount
end

-- 获取第index个字符对应在str中的字节索引(对应字符的首字节)
function string.get_byte_index(str, index)
	local curIndex = 0
	local i = 1
	local SubStrByteCount
	while curIndex < index do
		SubStrByteCount = string.get_utf8_byte_count(str, i)
		i = i + SubStrByteCount
		curIndex = curIndex + 1
	end
	return i - SubStrByteCount
end

function string.sub_utf8(str, startIndex, endIndex)
	local Len = string.utf8len(str)
	if startIndex < 0 then
		startIndex = Len + startIndex + 1
	end
	
	if endIndex and endIndex < 0 then
		endIndex = Len + endIndex + 1
	end

	if endIndex then
		return string.sub(str, string.get_byte_index(str, startIndex), string.get_byte_index(str, endIndex+1)-1)
	else
		return string.sub(str, string.get_byte_index(str, startIndex))
	end
end

--一个寻找字串低效的实现，注意不支持Pattern
function string.rfind(str, sub)
	local str1 = string.reverse(str)
	local sub1 = string.reverse(sub)
	local a,b = string_find(str1, sub1, 1, true)
	if a and b then
		return #str-b+1,#str-a+1
	end
end

function string.startswith(self,s2)
    return string_find(self,s2,1,true) == 1
end

function BoolToStr(value)
	return value and "true" or "false"
end

--判断是否字母或数字
function string.isalpha(n)
	--number
	if n >= 48 and n <= 57 then return true end
	--a ~ z
	if n >= 97 and n <= 122 then return true end
	--A ~ Z
	if n >= 65 and n <= 90 then return true end
	return false
end

--将一个str以del分割为若干个table中的元素
--n为分割次数
function string.split( line, sep, maxsplit ) 
	if not line or string.len(line) == 0 then
		return {}
	end
	sep = sep or ' '
	maxsplit = maxsplit or 0
	local retval = {}
	local idx = 0
	local pos = 1
	local step = 0
	local item, from, to
	while true do
		from, to = string_find(line, sep, pos, true)
		step = step + 1
		if (maxsplit ~= 0 and step > maxsplit) or from == nil then
			item = string_sub(line, pos)
			idx = idx + 1
			retval[idx] = item
			break
		else
			item = string_sub(line, pos, from-1)
			idx = idx + 1
			retval[idx] = item
			pos = to + 1
		end
	end
	return retval
end

if XEngine and XEngine.split_string then
	string.split = XEngine.split_string
end

function string.splitlines(text, maxsplit)
	return string.split(text,"\n",maxsplit)
end

--删除空白前导空白字符或者指定字符集中的字符
function string.lstrip(str, chars)
	if not chars then
		return string_gsub(str, "^%s*", "")
	end
	for k=1, #str do
		local sub = string_sub(str, k, k)
		if not string_find(chars, sub, 1, true) then
			return string_sub(str, k)
		end
	end
end

--删除空白后导空白字符或者指定字符集中的字符
function string.rstrip(str, chars)
	if not chars then
		return string_gsub(str, "%s*$", "")
	end
	for k=#str, 1,-1 do
		local sub = string_sub(str, k, k)
		if not string_find(chars, sub, 1, true) then
			return string_sub(str, 1, k)
		end
	end
end

--删除空白前后空白字符或者指定字符集中的字符
function string.strip(str, chars)
	return string.rstrip(string.lstrip(str, chars), chars)
end

--将字符串中的重复内容剪切成一份
--squeeze("1222322","2") -> "1232"
function string.squeeze(str, rep)
	if not str then return nil end
	local pos1, pos2 = string_find(str, rep, 1, true)
	if not pos1 or not pos2 then
		return str
	end

	local pos = pos2 + 1
	while(true) do
		local tmp1, tmp2 = string_find(str,rep,pos,true)
		if pos ~= tmp1 then
			local remain = string_sub(str, pos)
			local head = string_sub(str, 1, pos2)
			if head and remain then
				str = head .. string.squeeze(remain, rep)
			end
			break
		end
		pos = tmp2 + 1
	end
	return str
end

--判断一个字符串是否以$ends结尾
function string.endswith(str, ends)
	local i, j = string_find(str, ends, -#ends)
	return (i and j == #str)
end

--判断一个字符串是否以$begins开始
function string.beginswith(str, begins)
	local i, _ = string_find(str, begins, 1, true)
	return (i and i == 1)
end

function string.startswith( str, prefix )
	local from, _, _ = string_find( str, prefix, 1, true)
	return from == 1
end

function string.isnumber(str)
	return tostring(tonumber(str)) == str
end

-- 给定一个字符串集，给目标字符串中符合字符集的字符加上指定前缀
function string.add_header(str, keystrs, header)
	for key, _ in pairs(keystrs) do
		str = string_gsub(str, key, header..key)
	end
	return str
end

-- 将字符串中的数学函数关键字全部加上math.的前缀方便计算
-- "floor(a/b)+sin(c)" ==> "math.flor(a/b)+math.sin(c)"
function string.add_mathheader(str)
	return string.add_header(str, math, "math.")
end

function string.align_left(Str, MaxLen)
	local Fmt = "%-"..MaxLen.."s"
	return Fmt:format(Str)
end

function string.align_right(Str, MaxLen)
	local Fmt = "%"..MaxLen.."s"
	return Fmt:format(Str)
end

function string.align_center(Str, MaxLen)
	local Len = string.len(Str)
	if Len >= MaxLen then return Str end
	Str = string.align_right(Str, math.floor((MaxLen - Len) / 2) + Len)
	return string.align_left(Str, MaxLen)
end





math = math or {}
sys = sys or {}
sys.path = sys.path or {}
string = string or {}
os = os or {}
os.path = os.path or {}
nt = nt or {}
posix = posix or {}

local _env = os.getenv("OS")
if _env then
	local _, _, value = string_find( _env, "([wW]in)" )
	if string.upper(value) == 'WIN' then
		os.name = 'nt'
	else
		os.name = 'posix'
	end
else
	os.name = 'posix'
end

function math.round( num, idp )
	idp = idp or 0
	return tonumber( string.format("%."..idp.."f",num) )
end	

function math.round_up( num )
	return math.floor(num+0.5)
end

function math.toint( num )
	return tonumber( string.format("%d", num) )
end

function math.Vec3Add(P1, P2)
	local Ret = {}
	Ret[1] = P1[1] + P2[1]
	Ret[2] = P1[2] + P2[2]
	Ret[3] = P1[3] + P2[3]
	return Ret
end

function math.Vec3Sub(P1,P2)
	local Ret = {}
	Ret[1] = P1[1] - P2[1]
	Ret[2] = P1[2] - P2[2]
	Ret[3] = P1[3] - P2[3]
	return Ret
end

function math.Vec3Scale(P1, Scale)
	local Ret = {}
	Ret[1] = P1[1]*Scale
	Ret[2] = P1[2]*Scale
	Ret[3] = P1[3]*Scale
	return Ret
end

function math.normalize(Point)
	local s = 1/math.sqrt(Point[1]*Point[1] + Point[2]*Point[2] + Point[3]*Point[3])
	return {Point[1]*s, Point[2]*s, Point[3]*s}
end

function math.cross(V1, V2)
	local X = V1[2]*V2[3] - V1[3]*V2[3]
	local Y = V1[3]*V2[1] - V1[1]*V2[3]
	local Z = V1[1]*V2[2] - V1[2]*V2[1]
	return {X,Y,Z}
end

function math.dot(V1, V2)
	return V1[1]*V2[1] + V1[2]*V2[2] + V1[3]*V2[3]
end

local function _normalize(value)
	local retval = ''
	if type(value) == 'function' then
		retval = '<' .. tostring(value) .. '>'
	elseif type(value) == 'table' then
		retval = '<' .. tostring(value) .. '>'
	elseif type(value) == 'string' then
		retval = string.format('%q',value)
	else
		retval = tostring(value)
	end
	return retval
end

function sys.repr(value)
	local retval = ''
	if type(value) == 'table' then
		local visited = {}
		retval = retval .. '{'
		for i, v in ipairs(value) do
			retval = retval .. _normalize(v) .. ','
			visited[i] = 1
		end
		for k, v in pairs(value) do
			if not visited[k] then
				retval = retval .. '[' .. _normalize(k) .. '] = ' .. _normalize(v) .. ', '
			end
		end
		retval = retval .. '}'
		return retval
	else
		retval = _normalize(value)
	end
	return retval
end

local function dodump (value, c)
	local retval = ''
	if type(value) == 'table' then
		c = (c or 0) + 1
		if c >= 100 then error("sys.dump too deep:"..retval) end

		retval = retval .. '{'
		for k, v in pairs(value) do
			retval = retval .. '[' .. dodump(k, c) .. '] = ' ..dodump(v, c) .. ', '
		end
		retval = retval .. '}'
		return retval
	else
		retval = _normalize(value)
	end
	return retval
end
--为了防止死循环，不让它遍历超过100个结点。谨慎使用。
function sys.dump (value)
	local ni, ret = pcall (dodump, value)
	return ret
end

function os.path.exists( path )
	local f = io.open(path, "r")
	return f ~= nil
end

local _seps = {	nt = '\\', posix = '/',}

os.path.sep = _seps[os.name]

--[[
>>> print os.getcwd()
z:\trunk\logic\base
>>> d = os.path.join('.','z:/trunk')
>>> print d
z:/trunk
>>> d = os.path.join('.','\\')
>>> print os.getcwd()
z:\trunk\logic\base
>>> d = os.path.join('.','/')
>>> print d
/
freebsd
>>> d = os.path.join('.','\\')
>>> print d
./\
>>> d = os.path.join('.','/')
>>> print d
/
--]]
local function _join(a, b)
	if string.match(a,'^[ ]*$') then
		return b
	end
	if string_sub( b, 1, 1) == _seps.posix then
		return b
	else
		if os.name == 'nt' then
			if string.match( b, '^%a:' ) then
				return b
			end
		end

		if string_sub( a, -1 ) == os.path.sep then
			retval = a .. b
		else
			retval = a .. os.path.sep .. b
		end
		return retval
	end
	error("dont know how to join")
end

function os.path.join( ... )
	local retval = ''
	for i,v in ipairs(arg) do
		retval = _join( retval, v)
	end
	return retval
end

function splitdrive(p)
	--[[Split a pathname into drive and path specifiers. Returns a 2-tuple
	"(drive,path)";  either part may be empty]]
	if string_sub(p,2,2) == ':' then
		return string_sub(p,1,2), string_sub(p,3,string.len(p))
	end
	return '', p
end

function nt.normpath( path )
	local path = string_gsub(path, '/', '\\' )
	local prefix, path = splitdrive(path)
	-- We need to be careful here. If the prefix is empty, and the path starts
	-- with a backslash, it could either be an absolute path on the current
	-- drive (\dir1\dir2\file) or a UNC filename (\\server\mount\dir1\file). It
	-- is therefore imperative NOT to collapse multiple backslashes blindly in
	-- that case.
	-- The code below preserves multiple backslashes when there is no drive
	-- letter. This means that the invalid filename \\\a\b is preserved
	-- unchanged, where a\\\b is normalised to a\b. It's not clear that there
	-- is any better behaviour for such edge cases.
	if prefix == '' then
		-- No drive letter - preserve initial backslashes
		while string_sub(path,1,1) == "\\" do
			prefix = prefix .. "\\"
			path = string_sub(path,2,string.len(path))
		end
	else
		-- We have a drive letter - collapse initial backslashes
		if string.startswith(path,"\\") then
			prefix = prefix .. "\\"
			path = string_sub(path,2,string.len(path))
		end
	end

	local comps = string.split( path, "\\" )
	local i = 1
	while i < #comps + 1 do
		if comps[i] == '.' or comps[i] == '' then
			table_remove(comps,i)
		elseif comps[i] == '..' then
			if i > 0 and comps[i-1] ~= ".." then
				table_remove(comps,i)
				table_remove(comps,i-1)
				i = i - 1
			elseif i == 0 and string.startswith("\\") then
				table_remove(comps,i)
			else
				i = i + 1
			end
		else
			i = i + 1
		end
	end

	-- If the path is now empty, substitute '.'
	if not prefix and #comps == 0 then
		table.insert(comps,'.')
	end
	return prefix .. table.concat(comps,"\\")
end

-- Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
-- It should be understood that this may change the meaning of the path
-- if it contains symbolic links!

function posix.normpath( path )
	if path == '' then
		return '.'
	end

	local initial_slashes = string.startswith(path,'/') and 1 or 0
	-- POSIX allows one or two initial slashes, but treats three or more
	-- as single slash.
	if (initial_slashes ~= 0 and
		string.startswith(path,'//') and not string.startswith(path,'///')) then
		initial_slashes = 2
	end

	local comps = string.split(path,'/')
	local new_comps = {}
	local insert = table.insert
	for _,comp in ipairs(comps) do
		if comp ~= '' and comp ~=  '.' then
			if (comp ~= '..' or (not initial_slashes ~=0 and  #new_comps == 0) or
				(#new_comps ~= 0 and new_comps[-1] == '..')) then
				insert(new_comps,comp)
			elseif #new_comps ~= 0 then
				table_remove(new_comps, #new_comps)
			end
		end
	end

	comps = new_comps
	path = table.concat(comps,'/')
	if initial_slashes ~= 0 then
		path = string.rep('/',initial_slashes) .. path
	end
	return path or '.'
end

if os.name == 'nt' then
	os.path.normpath = nt.normpath
else
	os.path.normpath = posix.normpath
end

function os.getcwd()
	local retval = nil
	local cmd = nil

	if os.name == 'nt' then
		cmd = 'cd'
	else
		cmd = 'pwd'
	end
	local fin = io.popen(cmd,'r')
	retval = fin:read('*l')
	fin:close()	
	return retval
end

function os.path.abspath( path )
	if os.name == 'nt' then
		if string.match('^%a:',path) then
			return path
		end
	end

	local cwd = os.getcwd()
	if os.name == 'nt' then
		if _seps[ string_sub( path, 1, 1 ) ] ~= nil then
			return os.path.join(string_sub(cwd,1,2),path)
		end
	elseif os.name == 'posix' then
		if string_sub( path, 1, 1 ) == os.path.sep then
			return path
		end
	end

	return os.path.join(cwd,path)

	--[[
	python:
	>>> d = os.getcwd()
	>>> print d
	z:\trunk\logic\base
	>>> c = os.path.abspath('trunk')
	>>> print(c)
	z:\trunk\logic\base\trunk
	>>> d = os.path.abspath('./z:trunk')
	>>> print d
	z:\trunk\logic\base\z:trunk
	>>> d = os.path.abspath('\\')
	>>> print d
	z:\
	>>> d = os.path.abspath('/')
	>>> print d
	z:\
	--]]

end

function math.bresenham(x0, y0, x1, y1)
	-- please refer following linnk for detail information :
	-- http://en.wikipedia.org/w/index.php?title=Bresenham%27s_line_algorithm&printable=yes 
	-- the following implementation is devired from this article.

	local retval = {}
	local deltax = math.abs(x1 - x0)
	local deltay = math.abs(y1 - y0)
	local steep = deltay > deltax
	if steep then
		x0,y0 = y0,x0
		x1,y1 = y1,x1
		deltax, deltay = deltay, deltax
	end
	local xstep, ystep
	if x1 - x0 > 0 then xstep = 1 else xstep = -1 end
	if y1 - y0 > 0 then ystep = 1 else ystep = -1 end
	local y = y0
	local err = 0
	local insert = table.insert
	for x = x0, x1, xstep do
		if steep then
			insert(retval, {y,x})
		else
			insert(retval, {x,y})
		end
		err = err + deltay
		if err + err >= deltax then
			y = y + ystep
			err = err - deltax
		end
	end
	return retval
end
--返回一个数组的所有排列
--input {1, 2}
--output { {1, 2}, {2, 1} }
function math.permutation(Array)
	local PList = {}
	if table.getn(Array) < 1 then
		return nil
	end
	if table.getn(Array) == 1 then
		return {{Array[1]}}
	end
	local First = Array[1]
	table_remove(Array, 1)
	local Last = math.permutation(Array)
	local insert = table.insert
	for _, t in pairs(Last) do
		for i=1, table.getn(t) + 1 do
			local TmpList = {}
			--copy table
			for k, v in pairs(t) do
				TmpList[k] = v
			end
			insert(TmpList, i, First)
			insert(PList, TmpList)
		end
	end
	return PList
end

function math.randfloat(min, max)
	return min + (max - min) * math_random()
end





-------------------------------------------------------------------------------
--table库的扩展
-------------------------------------------------------------------------------

if not table.org_insert then
	table.org_insert = table.insert
end

table.insert = function(t, v1, v2)
	if not v2 then
		t[#t+1] = v1
		return t
	end
	return table.org_insert(t,v1,v2)
end

function table.back(tb)
	return tb[#tb]
end

function table.merge(dst, src, start, stop)
	stop = stop or #src
	start = start or 1
	local idx = #dst
	for i = start, stop do
		idx = idx + 1
		dst[idx] = src[i]
	end
	return dst
end

-- 洗牌算法，将一个数组随机打乱
function table.shuffle(tb, n)
	local random = math_random
	local j, tmp
	for i = #tb,1,-1 do
		j = random(1, i)
		tmp = tb[i]
		tb[i] = tb[j]
		tb[j] = tmp
		if n then
			n = n - 1
			if n <= 0 then break end
		end
	end
end

function table.choice(t)
	local i = math_random(1, #t)
	return t[i], i
end

function table.subarray(array, first, last)
	local ret = {}
	local idx = 0
	for i = first, last do
		idx = idx + 1
		ret[idx] = array[i]
	end
	return ret
end

function table.compare(a, b)
	if type(a) ~= "table" or type(b) ~= "table" then return false end
	local old
	for k, v in pairs(b) do
		old = a[k]
		if old ~= v then
			if type(old) == "table" and type(v) == "table" then
				if not table.compare(old, v) then return false end
			else
				return false
			end
		end
	end
	return true
end

function table.update(dst, src)
	if type(dst) ~= "table" or type(src) ~= "table" then return end
	for k, v in pairs(src) do
		dst[k] = v
	end
end

-- 带返回值
function table.update_ex(a, b)
	if type(a) ~= "table" or type(b) ~= "table" then return false end
	local change = false
	for k, v in pairs(b) do
		-- 标记已经变了，就直接赋值
		if change then
			a[k] = v
		else
			old = a[k]
			if not old then
				a[k] = v
				change = true
			elseif old ~= v then
				if type(old) == "table" and type(v) == "table" then
					if not table.compare(old, v) then
						a[k] = v
						change = true
					end
				else
					a[k] = v
					change = true
				end
			end
		end
	end
	return change
end

-- 将数组b的内容追加到数组a中
function table.append(a, b)
	if type(a) ~= "table" or type(b) ~= "table" then return end
	if not next(b) then return end
    local idx = #a
	for k, v in ipairs(b) do
		a[idx + k] = v
	end
end

--查看Var落在哪个区间
-- Array : { [1] = {Limit = 10}, [2] = {Limit = 20}, [3] = {}} , i.e: 0-10, 11-20, 20-
function table.in_range (Var, Range)
	for i, v in ipairs (Range) do
		if not v.Limit or Var <= v.Limit then
			return i, v
		end
	end
end

function table.member_key(tb, value)
	if not tb then return end
	for k, v in pairs(tb) do
		if v == value then
			return k
		end
	end
end

function table.has_value(t, val)
	if not t or not next(t) then return false end
	for k, v in pairs(t) do
		if v == val then return true end
	end
	return false
end

--返回所有的key，作为一个数组，效率比较低，不建议频繁调用
function table.keys(tb)
	if not tb then return end
	local keys = {}
	local idx = 0
	for k, _ in pairs(tb) do
        idx = idx + 1
		keys[idx] = k
	end
	return keys
end

--返回所有的value，作为一个数组,效率比较低，不建议频繁调用
function table.values(tb)
	if not tb then return end
	local values = {}
	local idx = 0
	for _, v in pairs(tb) do
        idx = idx + 1
		values[idx] = v
	end
	return values
end

--返回一个随机的key
function table.random_key(tb)
	if not next(tb) then return end
	local keys = table.keys(tb)
	local n = table.maxn(keys)
	if n <= 0 then return end
	return keys[math_random(1,n)]
end

--从table中随机返回n个value
function table.random_values(tbl, n)
	local n = n or 1
	local ret = table.values(tbl)
	local size = #ret
	while size > n do
		table_remove(ret, math_random(1, size))
		size = size - 1
	end
	return ret
end

--对Array(key)进行随机排序
--不改变参数Array的内容，排序的结果通过返回值返回, 并返回排序前后的key的对应关系
function table.random_sort(array)
	local n = #array

	local k = {}
	for i = 1, n do
		k[i] = i
	end

	local o = {}
	local s = {}
	for i = 1, n do
		local j = math_random (n - i + 1)
		s[k[j]] = i
		o[i] = array[k[j]]
		table_remove (k, j)
	end

	return o, s
end

--从一个mapping中随机出几个k,v对组成新的mapping
function table.random_kv(Table, n)
	local n = n or 1
	local Keys = table.keys(Table)
	if n > #Keys then
		return Table
	end
	local Ret = {}
	for i=1, n do
		local Rand = math_random(1, #Keys)
		local RandKey = Keys[Rand]
		--Ret[RandKey] = Table[RandKey]
		Ret[i] = Table[RandKey]
		table_remove(Keys, Rand)
	end
	return Ret
end

--从table中随机返回1个value
function table.random_value(Table)
	local Values = table.values(Table)
	local n = table.maxn(Values)
	if n <= 0 then
		return nil
	end
	return Values[math_random(1,n)]
end

--从符合ipairs迭代的table中随机返回一个value
function table.random_array_value(tbl)
	local size = #tbl
	if size == 0 then return end
	return tbl[math_random(1, size)]
end

function table.random_array_values(tbl, amount)
	local size = 0
	local ret = {}
	for _, v in ipairs(tbl) do
		size = size + 1
		ret[size] = v
	end
	while size > amount do
		table_remove(ret, math_random(1, size))
		size = size - 1
	end
	return ret
end

function table.filter(tb, value)
	for i=#tb, 1, -1 do
		if tb[i] == value then
			table_remove(tb, i)
		end
	end
end

function table.remove_array_value(tb, value)
	if not tb then return end
	local removed = false
	local size = #tb
	if size == 0 then return false end
	for i = size, 1, -1 do
		if tb[i] == value then
			table_remove(tb, i)
			removed = true
		end
	end
	return removed
end

function table.remove_by_value(Tbl, Value)
	local Removed = false
	for k,v in pairs(Tbl) do
		if v == Value then
			Tbl[k] = nil
			Removed = true
		end
	end
	return Removed
end

function table.remove_ex (tb,value)
	for k,v in pairs(tb) do
		if v == value then
			table_remove(tb,k)
			return
		end
	end
end

-- 是否连续数组（中间没有nil值的数组）
function table.is_regular_array(tb)
	local TableSize = table.size(tb)
	local ArraySize = 0
	for _ in ipairs(tb) do
		ArraySize = ArraySize + 1
	end
	return TableSize == ArraySize, ArraySize, TableSize
end

-- 用于安全的加载数据文件，即在空的全局环境中执行指定的脚本文件
function safe_dofile(fname)
	if type(fname) ~= "string" then
		return
	end
	local function loader(path)
		local succed, func, err = pcall(loadfile, path)
		if succed and type(func) == "function" then
			return func()
		else
			return nil, err
		end
	end
	local env = { }
	setmetatable(env, {__index = _G})
	return setfenv(loader, env)(fname)
end

function try_dofile(fname)
	if type(fname) ~= "string" then
		return
	end
	local function loader(path)
		local succed, func, err = pcall(loadfile, path)
		if succed and type(func) == "function" then
			return func()
		end
	end
	local env = { }
	setmetatable(env, {__index = _G})
	return setfenv(loader, env)(fname)
end

--重写系统的unpack
_ORG_UNPACK = unpack
function unpack(tb, from, to)
	return _ORG_UNPACK(tb, from or 1, to or table.maxn(tb))
	-- if from then
	-- 	return _ORG_UNPACK(tb, from, to or table.maxn(tb))
	-- else
	-- 	return _ORG_UNPACK(tb, 1, table.maxn(tb))
	-- end
end

------------------------------------------------------
--序列化和反序列化table，只保存number和string类型成员
function table.save(tb, path, tb_deep, is_compile)
	if type(tb) ~= "table" or type(path) ~= "string" then
		print("invalid arg for table.save:", tb, path, tb_deep)
		return
	end

	local mode = is_compile and "wb" or "w"
	local file = io.open(path, mode)
	if not file then return false end
	tb_deep =  tb_deep or 20
	local cur_deep = 0
	local tb_cache = {}
	local function save_table(tb_data)
		-- 存储当前层table
		if type(tb_data) ~= "table"  then
			error("存储类型必须为table:")
			return
		end
		if tb_cache[tb] then
			error("无法继续存储，table中包含循环引用，")
			return
		end
		cur_deep = cur_deep + 1
		if cur_deep > tb_deep then
			error("待存储table超过可允许的table深度")
			cur_deep = cur_deep -  1
			return
		end
		local tab = string.rep(" ", (cur_deep-1)*4)
		local str = "{\n"

		-- 调整table存储顺序，按照key排序
		local keys = {}
		local insert = table.insert
		for k, v in pairs(tb_data) do
			if type(k) == "number" then
				insert(keys, k)
			end
		end
		table.sort(keys)
		for k, v in pairs(tb_data) do
			if type(k) ~= "number" then
				insert(keys, k)
			end
		end

		-- 保存调整后的table
		local i, v
		for i, k in ipairs(keys) do
			v = tb_data[k]
			local arg, value
			if type(k) == "number" then
				arg = string.format("[%d]", k)
			end
			if type(k) == "string" then
				arg = string.format("[\"%s\"]", k)
			end
			if type(k) == "boolean" then
				value = tostring(k)
			end
			if type(v) == "number" then
				value = string.format("%d", v)
			end
			if type(v) == "string" then
				value = string.format("\"%s\"", v)
			end
			if type(v) == "table" then
				value = save_table(v)
			end
			if type(v) == "boolean" then
				value = tostring(v)
			end
			if arg and value then
				str = str..string.format("%s%s = %s,\n", tab, arg, value)
			end
		end
		tb_cache[tb_data] = true
		cur_deep = cur_deep -  1
		return str..tab.."}"
	end
	local tb_str = "return \n"..save_table(tb)
	file:write(tb_str)
	file:close()
	print("table save succed", tb, path)
	return true
end

function table.load(path)
	return safe_dofile(path)
end

--src dst都是数组
function table.array_append (dst, src)
	if not src or not next(src) then return dst end
	local size = #dst
	for idx, v in ipairs (src) do
		dst[size + idx] = v
	end
	return dst
end

--返回Array中的最大值
--注意:不是Hash-table
function table.max(Array)
	return math.max (unpack(Array))
end

--返回Array中的最小值
--注意:不是Hash-table
function table.min(Array)
	return math.min (unpack(Array))
end

--返回table的size
function table.size(Table)
	if Table then
		local Ret = 0
		for _,_ in pairs(Table) do
			Ret = Ret + 1
		end
		return Ret
	else
		return 0
	end
end

--返回number型数组的平均值(float)
function table.avg(Array)
	if #Array == 0 then
		return 0
	end
	local All = 0
	local i = 0
	for _, Data in ipairs(Array) do
		assert(type(Data) == "number")
		All = All + Data
		i = i + 1
	end
	return All/i
end

function table.empty(tbl)
	return next(tbl) == nil
end

function table.not_empty(tbl)
	return next(tbl) ~= nil
end

--判断两个map的内容是否相同，一个简单而低效的实现
--效率较低，慎用
function table.equal_map(tbl1, tbl2)
	if tbl1 == tbl2 then
		return true
	end
	if not tbl1 or not tbl2 then
		return false
	end
	for k,v in pairs(tbl1) do
		if tbl2[k] ~= v then
			return false
		end
	end
	for k,v in pairs(tbl2) do
		if tbl1[k] ~= v then
			return false
		end
	end
	return true
end

function table.sort_by_value(tbl)
	local keys = table.keys(tbl)
	table.sort(keys, function(x, y)
		return tbl[x] < tbl[y]
	end)
	return keys
end

function table.heap_sort(tbl, top_n, cmp_func)
    local function adjust_heap(heap, len, idx)
        while math.floor(len/2) >= idx do
            local child = idx * 2
            if (child+1) <= len and cmp_func(heap[child], heap[child+1]) then
                child = child + 1
            end
            if cmp_func(heap[idx], heap[child]) then
                local tmp = heap[idx]
                heap[idx] = heap[child]
                heap[child] = tmp

                idx = child
            else
                break
            end
        end
    end

    local function build_heap(heap, len)
        for idx=math.floor(len/2), 1, -1 do
            adjust_heap(heap, len, idx)
        end

    end

    if top_n == 1 then
        local aid = nil
        for key, value in pairs(tbl) do
            if not aid then
                aid = value
            else
                if cmp_func(value, aid) then
                    aid = value
                end
            end
        end

        return {aid}
    end

    local tbl_len = 0
	local heap = {}
	local insert = table.insert
    for key, value in pairs(tbl) do
        tbl_len = tbl_len + 1
        if tbl_len < top_n then
            insert(heap, value)
        elseif tbl_len == top_n then
            insert(heap, value)
            build_heap(heap, #heap)
        else
            if cmp_func(value, heap[1]) then
                heap[1] = value
                adjust_heap(heap, #heap, 1)
            end
        end
    end

    if tbl_len < top_n then
        build_heap(heap, #heap)
    end

    local len = #heap
    for i=1, len, 1 do
        local idx = len-i+1
        local tmp = heap[idx]
        heap[idx] = heap[1]
        heap[1] = tmp

        build_heap(heap, len-i)
    end

    return heap
end

-- return a copy of table<tbl>, just copy the data or the top level.
function table.clone(tbl)
	local ret = {}
	if type(tbl) ~= "table" then
		return ret
	end
	for k, v in pairs(tbl) do
		ret[k] = v
	end
	return ret
end

-- return a deep copy of table<src>
function table.deep_clone(tbl)
	if type(tbl) ~= "table" then
		return tbl
	end
	local level = 0
	local function clone_table(t)
		level = level + 1
		if level > 20 then
			--for a,b in pairs(src) do print(a,b) end -- for debug
			error("table clone failed, source table is too deep!")
		end
		local ret = {}
		for k, v in pairs(t) do
			if type(v) == "table" then
				ret[k] = clone_table(v)
			else
				ret[k] = v
			end
		end
		level = level - 1
		return ret
	end
	return clone_table(tbl)
end

-- 合并table
function table.merge_ex(Src,Dst)
	if type(Src) ~= "table" or type(Dst) ~= "table" then
		return Src
	end

	local level = 0
	local function merge_table(s,d)
		level = level + 1
		if level > 20 then
			error("table merge failed, Dst table is too deep!")
		end

		for k, v in pairs(d) do
			if not s[k] or type(v) ~= "table" then
				s[k] = v
			else
				merge_table(s[k],v)
			end
		end
		level = level - 1
	end

	return merge_table(Src,Dst)
end

function table.print(t, tname, print_one_level)
	if type(t) ~= "table" then
		--err_info("deep_print error, parameter accept is not a table!")
		return
	end
	local info = debug.getinfo(2)
    if info then
        print(string.format("[INFO] [file]=%s,[line]=%d: print table >>",info.source or "?",info.currentline or 0))
    end

	local _deep_count = 0
	local print_one_table
	local printed_tables = {}
	local t_path = {}
	tname = tname or "root_table"
	local insert = table.insert

    local function output_value(value)
        local str, value_type
        value_type = type(value)
        if value_type == "number" then
            str = string.format("[ %s ]n", value)
        elseif value_type == "string" then
            str = string.format("[ \"%s\" ]s", value)
        elseif value_type == "table" then
            str = string.format("[ 0x%s ]t", string_sub(tostring(value), 8))
        elseif value_type == "function" then
            str = string.format("[ 0x%s ]f", string_sub(tostring(value), 11))
        elseif value_type == "userdata" then
            str = string.format("[ 0x%s ]u", string_sub(tostring(value), 11))
        else
            str = string.format("[ S\"%s\" ]%s", tostring(value), type(value))
        end
        return str
    end

	local function print_one_table(tb, tb_name, print_one_level)
		tb_name = tb_name or "table"
		insert(t_path, tb_name)
		local tpath = ""
		for i, pname in pairs(t_path) do
			tpath = tpath.."."..pname
		end
		printed_tables[tb] = tpath
		_deep_count = _deep_count + 1
		local str
		local tab = string.rep(" ", _deep_count*4)
		--print(string.format("%s  [ 0x%s ]t\n%s  {    ", tab, string_sub(tostring(tb), 8), tab))
		print(string.format("%s  {", tab))
		for k, v in pairs(tb) do
			if type(v) == "table" then
				if printed_tables[v] then
					str = string.format("%s    %s = [ %s ]t", tab, output_value(k), printed_tables[v])
					print(str)
				elseif not print_one_level then
					str = string.format("%s    %s = ", tab, output_value(k))
					print(str)
					print_one_table(v, tostring(k))
				else
					str = string.format("%s    %s = %s", tab, output_value(k), output_value(v))
					print(str)
				end
			else
				str = string.format("%s    %s = %s", tab, output_value(k), output_value(v))
				print(str)
			end
		end
		print(tab.."  }")
		table_remove(t_path)
		_deep_count = _deep_count - 1
	end

	print_one_table(t, tname, print_one_level)
	printed_tables = nil
end

function table.format(tb, deep)
    if type(tb) ~= "table" then return tb end
    if not deep then deep = 0 end

    local blank = ""
    for i = 1, deep do
        blank = blank .. "    "
    end

    local result = ""
    result = result .. "{\n"
    for k, v in pairs(tb) do
        local type_key = type(k)
        local type_value = type(v)

        local str_key, str_value

        if type_key == "number" then
            str_key = tostring(k)
        elseif type_key == "string" then
            str_key = string.format("\"%s\"", k)
        end

        if type_value == "number" then
            str_value = tostring(v)
        elseif type_value == "string" then
            str_value = string.format("\"%s\"", v)
        elseif type_value == "boolean" then
            str_value = v and "true" or "false"
        elseif type_value == "function" then
            str_value = string.format("[0x%s]f", string_sub(tostring(v), 11))
        elseif type_value == "userdata" then
            str_value = string.format("[0x%s]u", string_sub(tostring(v), 11))
        elseif type_value == "table" then
            str_value = table.format(v, deep + 1)
        end

        result = result .. blank
        result = result .. string.format("    [%s] = %s,\n", str_key, str_value)
    end
    result = result .. blank .. "}"

    return result
end

function table.ifind(tb, value)
	if not tb then return end
	for k, v in ipairs(tb) do
		if value == v then
			return k
		end
	end
end

function table.clear(tbl)
    for i = 1, #tbl do
		table_remove(tbl)
    end
    return tbl
end

function table.tostring(tbl, deep)
	local max_deep = deep or 10
	local _deep_count = 0

	local function tab2str(tab)
		if tab == nil then
			return
		end

		assert(_deep_count < max_deep, "deep > max")
		_deep_count = _deep_count + 1

		local result = "{"
		for k,v in pairs(tab) do
			if type(k) == "number" then
				if type(v) == "table" then
					result = string.format( "%s[%d]=%s,", result, k, tab2str(v) )
				elseif type(v) == "number" then
					result = string.format( "%s[%d]=%d,", result, k, v )
				elseif type(v) == "string" then
					result = string.format( "%s[%d]=%q,", result, k, v )
				elseif type(v) == "boolean" then
					result = string.format( "%s[%d]=%s,", result, k, tostring(v) )
				else
					error("the type of value is a function or userdata")
				end
			else
				if type(v) == "table" then
					result = string.format( "%s['%s']=%s,", result, k, tab2str(v) )
				elseif type(v) == "number" then
					result = string.format( "%s['%s']=%d,", result, k, v )
				elseif type(v) == "string" then
					result = string.format( "%s['%s']=%q,", result, k, v )
				elseif type(v) == "boolean" then
					result = string.format( "%s['%s']=%s,", result, k, tostring(v) )
                elseif type(v) == "userdata" and v == JSON.null then
				else
					error("the type of value is a function or userdata")
				end
			end
		end
		result = result .. "}"
		_deep_count = _deep_count - 1
		return result
	end

	return tab2str(tbl)
end



-- 将明文字符串或者table，转换成object
function Table2Object(data, parentObj)
    local dataType = type(data)
	if dataType == "string" then
		data = loadstring("return " .. data)()
        dataType = type(data)
	end
	assert(dataType == "table")
    local cls, obj
	if data.__class_type__ then
		cls = ALL_CLASS[data.__class_type__]
        if not cls then return end
		obj = cls:New()
		if data.__init_args__ then
			obj[1] = Table2Object(data.__init_args__, obj)
		end
		if data.__temp_args__ then
			obj[0] = Table2Object(data.__temp_args__)
		end
        return obj
	end

	for k, v in pairs(data) do
		if type(v) == "table" then
			if v.__class_type__ then
				cls = ALL_CLASS[v.__class_type__]
                if cls then
					local parentClass = parentObj and ALL_CLASS[parentObj:GetType()]
					obj = cls:New()
					if parentObj and parentObj.__dirtydata_flag and parentClass.__savevar_class and parentClass.__savevar_class[k] then
						--table.print(parentClass.__savevar_path)
						if not obj.__dirtydatafunc_inited then
							cls:RegDirtyDataByID(parentObj.__dbservice_id)
							cls:RegDirtyDataFunc(parentObj.__savevar_path[k])
						end
						if not obj.__dirtydata_root then
							obj.__dirtydata_flag = parentObj.__dirtydata_flag
							obj.__dirtydata_root = parentObj.__dirtydata_root or parentObj
						end
					end
					if v.__init_args__ then
						obj[1] = Table2Object(v.__init_args__, obj)
					end
					if v.__temp_args__ then
						obj[0] = Table2Object(v.__temp_args__)
					end
                    data[k] = obj
                end
			else
				data[k] = Table2Object(v)
			end
		end
	end
	return data
end

-- if XEngine and XEngine.serialize_complex then
-- 	table.tostring = XEngine.serialize_complex
-- end
-- _G.Serialize = XEngine.serialize_complex
-- _G.UnSerialize = Table2Object
-- _G.Pack = XEngine.pack_complex
-- _G.UnPack = XEngine.unpack




function table.loadstring(str)
	local chunk = loadstring("return "..(str or "{}"))
	if chunk then
		local rt = chunk()
		rt = (rt and type(rt) == "table") and rt or {}
		return rt
	else
		error("Invalid table string %s",str)
	end
end

function table.itemtostring(t)
	local strList = {}
	if t then
		for k, v in ipairs(t) do
			strList[k] = tostring(v)
		end
	end
	return strList
end

function math.limit( value, min, max)
	if value < min then
		return min
	elseif value >  max then
		return max
	else
		return value
	end
end

local DAY_CHANGE_HOUR = 5
local FIRST_DAY_TIME = os_time({
	year = 1970,
	month = 1,
	day = 2,
	hour = DAY_CHANGE_HOUR,
})

function os.daydiff(from, to, dayChangeHour)
	local firstDayTime = FIRST_DAY_TIME
	if dayChangeHour and dayChangeHour ~= DAY_CHANGE_HOUR then
		firstDayTime = os_time({
			year = 1970,
			month = 1,
			day = 2,
			hour = dayChangeHour,
		})
	end
	local toDay = math.floor((to - firstDayTime) / 86400)
	local fromDay = math.floor((from - firstDayTime) / 86400)
	return toDay - fromDay
end

function os.daydiffex(from, to, hour, min)
	local diff = to - from
	local daydiff = math.floor(diff / 86400)

	from = from + daydiff * 86400
	local date = os_date("*t")
	date.hour = hour
	date.min = min
	date.sec = 0
	local time = os_time(date)
	if from < time and to > time then
		daydiff = daydiff + 1
	end

	return daydiff
end

function os.zeroclock( time )
	-- 该时间的0点
	local time = time or os_time()
	local data = os_date('*t', time)
	data.hour = 0
	data.min  = 0
	data.sec  = 0
	return os_time( data )
end

-- time所在周的周一0点的时间戳
function os.weekzeroclock( time )
	local time = time or os_time()
	local data = os_date('*t', time)
	local wday = data.wday
	local daydiff
	if wday >= 2 then
		daydiff = wday - 2
	else
		daydiff = 7 - wday
	end

	time = time - 24 * 3600 * daydiff
	return os.zeroclock(time)
end

--// CHILL CODE ? //--

--// table.binsert( table, value [, comp] )

-- LUA 5.x ADD-On for the table library
-- Inserts a given value through BinaryInsert into the table sorted by [,comp]
-- If comp is given, then it must be a function that receives two table elements,
-- and returns true when the first is less than the second or reverse
-- e.g.  comp = function( a, b ) return a > b end , will give a sorted table, with the biggest value on position 1
-- [, comp] behaves as in table.sort( table, value [, comp] )

-- This method is faster than a regular table.insert( table, value ) and a table.sort( table [, comp] )

function table.binsert(t, value, fcomp)
	fcomp = fcomp or function( a, b ) return a < b end
	local iStart, iEnd, iMid, iState =  1, table.getn(t), 1, 0
	local floor = math.floor
	while iStart <= iEnd do
		iMid = floor((iStart + iEnd) / 2)
		if fcomp(value, t[iMid]) then
			iEnd = iMid - 1
			iState = 0
		else
			iStart = iMid + 1
			iState = 1
		end
	end
	local pos = iMid + iState
	table.insert(t, pos, value)
	return pos
end

--// table.bfind( table, value [, compvalue] [, reverse] )

-- LUA 5.x ADD-On for the table library
-- Searches the table through BinarySearch for value, if the value is found it returns the index
--     and the value of the table where it was found
-- If compvalue is given then it must be a function that takes one value and returns a second value2
-- to be compared with the input value, e.g. compvalue = function( value ) return value[1] end
-- If reverse is given then the search assumes that the table is sorted with the biggest value on position 1

function table.bfind( t, value, fcompval, reverse )

	-- initialise Functions
	fcompval = fcompval or function( value ) return value end
	fcomp = function( a, b ) return a < b end
	if reverse then
		fcomp = function( a, b ) return a > b end
	end

	--  Initialise Numbers
	local iStart, iEnd, iMid =  1, table.getn( t ), 1

	-- Binary Search
	while (iStart <= iEnd) do

		-- calculate middle
		iMid = math.floor( ( iStart + iEnd )/2 )

		-- get compare value
		local value2 = fcompval( t[iMid] )

		if value == value2 then
			return iMid, t[iMid]
		end

		if fcomp( value , value2 ) then
			iEnd = iMid - 1
		else
			iStart = iMid + 1
		end
	end
end

--传入一个类似这样的table：{[1] = {Odds = 50}, [2] = {Odds = 50}}
--根据随机取到key返回
function table.get_key_by_odds(Tbl, FullOdds)
	if Tbl == nil then
		return nil
	end
	--兼容某些传入的是小数的情况
	local Ext=100
	if not FullOdds then
		FullOdds = 0
		for k,v in pairs(Tbl) do
			FullOdds = FullOdds + v.Odds*Ext
		end
	else
		FullOdds = FullOdds*Ext
	end

	--注意，nil和无参数是不一样的
	local Ran = math_random(FullOdds)

	local TotalRan = 0
	for key, subTbl in pairs(Tbl) do
		TotalRan = TotalRan + subTbl.Odds*Ext
		if Ran <= TotalRan then
			return key
		end
	end
end

--判断包含关系(array only)
function table.contain(Big, Small)
	for _, Each in pairs(Small) do
		if not table.member_key(Big, Each) then
			return false, Each
		end
	end

	return true
end

--将Array1与Array2里面对应位置合成mapping的一个pair
function table.mapping(Array1, Array2)
	local Ret = {}
	for k,v in ipairs(Array1) do
		Ret[v] = Array2[k]
	end
	return Ret
end

--将一个array的前n个元素随机排列
function table.random_table(Table, n)
    local count = #Table
	for i = 1, n do
		local R = r(i, count)
        if R ~= i then
            Table[i], Table[R] = Table[R], Table[i]
        end
    end
    return Table
end

--将参数里面的keylist合成一个deep map
--比如传入 'a','b','c','d'则会return 一个map {a={b={c='d'}}}
function table.keymap(map, ...)
	map = map or {}
	if arg['n'] <= 1 then
		return map
	end

	local tmp = map
	for k,v in ipairs(arg) do
		if k >= arg['n'] - 1 then
			tmp[v] = arg[k+1]
			break
		end

		tmp[v] = tmp[v] or {}
		tmp = tmp[v]
	end
	return map
end

function table.reverse(Array)
	local size = #Array
	for i=1, math.floor(size/2) do
		local tmp = Array[i]
		Array[i] = Array[size+1-i]
		Array[size+1-i] = tmp
	end
	return Array
end

--设置一个Table为只读
function table.readonly(t, all)
	local mt = getmetatable(t) or {}
	mt.__newindex = function (t,k,v)
		error("attempt to update a read-only table",3)
	end
	setmetatable(t, mt)
	if all then
		for k, v in pairs(t) do
			if type(k) == "table" then
				table.readonly(k, all)
			end
			if type(v) == "table" then
				table.readonly(v, all)
			end
		end
	end
	return t
end

-- input {{"1","一",}, {"2","二"}, {"3","三"}}
-- output example as "一2三"
-- 组合函数
function table.combo(src)
	local function concat(a, b)
		local tbl = {}
		local idx = 0
		for _,v1 in pairs(a) do
			for _,v2 in pairs(b) do
				idx = idx + 1
				tbl[idx] = string.format("%s%s", v1,v2)
			end
		end
		return tbl
	end
	local tbl = src[1]
	for k=2, #src do
		tbl = concat(tbl, src[k])
	end
	return tbl
end

-- 权重随机
function table.random_by_weight(tb, WeightKeyWord)
	local WeightKeyWord = WeightKeyWord or "Weight"
	local totalWeight = 0
	for _, info in pairs(tb) do
		totalWeight = totalWeight + (info[WeightKeyWord] or 0)
	end

	local randomValue = math_random(0, totalWeight)
	local tmpWeight = 0
	for _, info in pairs(tb) do
		tmpWeight = tmpWeight + (info[WeightKeyWord] or 0)
		if randomValue <= tmpWeight then
			return info
		end
	end
end

-- 权重随机
-- 把value当权重，随机key
-- 可传入 totalWeight 来指定总权重
function table.random_key_by_value(tb, totalWeight)
	local totalWeight = totalWeight or 0
	if totalWeight == 0 then
		for _, weight in pairs(tb) do
			totalWeight = totalWeight + (weight or 0)
		end
	end
	if totalWeight == 0 then
		return
	end

	local randomValue = math_random(1, totalWeight)
	local tmpWeight = 0
	for key, weight in pairs(tb) do
		tmpWeight = tmpWeight + (weight or 0)
		if randomValue <= tmpWeight then
			return key
		end
	end
end

-- 数组中，是否有某个值的重复次数达到了传入参数
-- 返回所有达到限制的值组成的table
function table.check_repeat_count(tb, limit)
	local CountList = {}
	for _, value in pairs(tb) do
		CountList[value] = (CountList[value] or 0) + 1
	end

	local res = {}
	local insert = table.insert
	for value, count in pairs(CountList) do
		if count >= limit then
			insert(res, value)
		end
	end

	return res
end

function table.random_take_out(tbl)
	if tbl and #tbl > 0 then
		return tbl[math.random(1,#tbl)]
	end
end

-- 返回[1,max_num]之间的随机n个不重复数字
function get_random_num(amount, max_num)
	local r = {}
	if amount >= max_num then
		for i = 1, amount do
			r[i] = i
		end
	else
        local math_random = math_random
        local insert = table.insert
        if amount > (max_num / 3 * 2) then
            local tmp_arr = {}
            for i = 1, max_num do
                tmp_arr[i] = i
            end
            for i = 1, amount do
                local len = max_num - i + 1
                local rk = math_random(len)
                local tmp = tmp_arr[rk]
                tmp_arr[rk] = tmp_arr[len]
                tmp_arr[len] = tmp
                insert(r, tmp)
            end
        else
            local check = {}
            while #r < amount do
                local rk = math_random(max_num)
                if not check[rk] then
                    insert(r, rk)
                    check[rk] = true
                end
            end
        end
	end
	return r
end

function IsSameDay(Time1, Time2, hour)
	hour = hour or DAILY_HOUR
	-- local Date1 = os_date("*t", Time1)
	-- local Date2 = os_date("*t", Time2)
	-- if Date1.year == Date2.year and Date1.month == Date2.month and Date1.day == Date2.day then
	-- 	return true
	-- end
	local DayNo1 = TIME.GetRealDayNo(Time1, hour)
	local DayNo2 = TIME.GetRealDayNo(Time2, hour)
	if DayNo1 == DayNo2 then
		return true
	end
end


_ORG_XPCALL = xpcall
xpcall = function (call_func, err_func, ...)
	err_func = err_func or debug.excepthook
	return _ORG_XPCALL(call_func, err_func, ...)
end

function GetStringCharCount(str)
    local lenInByte = #str
    local charCount = 0
    local i = 1
    while (i <= lenInByte)
    do
       local curByte = string.byte(str, i)
        local byteCount = 1;
        if curByte > 0 and curByte <= 127 then
           byteCount = 1                                               --1字节字符
        elseif curByte >= 192 and curByte < 223 then
           byteCount = 2                                               --双字节字符
        elseif curByte >= 224 and curByte < 239 then
           byteCount = 3                                               --汉字
        elseif curByte >= 240 and curByte <= 247 then
           byteCount = 4                                               --4字节字符
        end
        
        string_sub(str, i, i + byteCount - 1)
        i = i + byteCount                                               -- 重置下一字节的索引
        charCount = charCount + 1                                       -- 字符的个数（长度）
    end
    return charCount
end



-- lbase64加密和解密算法
local BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
function Base64Encode(source_str)
    local s64 = ""
    local str = source_str
    while #str > 0 do
        local bytes_num = 0
        local buf = 0
        for byte_cnt=1,3 do
            buf = (buf * 256)
            if #str > 0 then
                buf = buf + string.byte(str, 1, 1)
                str = string_sub(str, 2)
                bytes_num = bytes_num + 1
            end
        end
        for group_cnt=1,(bytes_num+1) do
            local b64char = math.fmod(math.floor(buf/262144), 64) + 1
            s64 = s64 .. string_sub(BASE64_CHARS, b64char, b64char)
            buf = buf * 64
        end
        for fill_cnt=1,(3-bytes_num) do
            s64 = s64 .. '='
        end
    end
    return s64
end

function Base64Decode(str64)
    local temp={}
    for i=1,64 do
        temp[string_sub(BASE64_CHARS,i,i)] = i
    end
    temp['=']=0
    local str=""
    for i=1,#str64,4 do
        if i>#str64 then
            break
        end
        local data = 0
        local str_count=0
        for j=0,3 do
            local str1=string_sub(str64,i+j,i+j)
            if not temp[str1] then
                return
            end
            if temp[str1] < 1 then
                data = data * 64
            else
                data = data * 64 + temp[str1]-1
                str_count = str_count + 1
            end
        end
        for j=16,0,-8 do
            if str_count > 0 then
                str=str..string.char(math.floor(data/math.pow(2,j)))
                data=math.mod(data,math.pow(2,j))
                str_count = str_count - 1
            end
        end
    end

    local last = tonumber(string.byte(str, string.len(str), string.len(str)))
    if last == 0 then
        str = string_sub(str, 1, string.len(str) - 1)
    end
    return str
end

-- 分割字符串成为table的扩展
-- Type:	1-字符型	2-数字型	3-布尔型	4-优先数字型
function string.split2table(Txt, Char, Type)
	if not Txt then return {} end
	Char = Char or ","
	Type = Type or 1

	local Ret = string.split(Txt, Char)
	if not Ret then return {} end
	
	if Type == 1 then
	elseif Type == 2 then
		for i, v in ipairs(Ret) do
			Ret[i] = tonumber(v)
		end
	elseif Type == 3 then
		for i, v in ipairs(Ret) do
			if v == "1" or v == "true" then
				Ret[i] = true
			else
				Ret[i] = false
			end
		end
	elseif Type == 4 then
		for i, v in ipairs(Ret) do
			local NumValue = tonumber(v)
			if NumValue then
				Ret[i] = NumValue
			end
		end
	end
	return Ret
end



-------------------------------------------------------------------------------
-- 文件相关操作
-------------------------------------------------------------------------------

--创建path中的各级目录
function Mkdir(path)
	if posix.stat(path) then return true end
	local start = 1
	local ret, errmsg, errno
	while true do
		local st, et = string.find(path, "%/", start)
		if st and et then
			local tmpPath = string.sub(path, 1, et)
			if not posix.stat(tmpPath) then
				ret, errmsg, errno = posix.mkdir(tmpPath)
				if ret == nil and errno then
					return false, errmsg
				end
			end
			start = et + 1
		else
			break
		end
	end
	return true
end

-- 判断目录是否拥有读写权限，如果目录不存在就创建
-- w：写 r：读 x：执行 f：是否存在
function MkdirAndCheck(path)
	local ret, errmsg = Mkdir(path)
	if not ret then return false, errmsg end
	local ret, errmsg, errno = posix.access(path, "wrf")
	if ret == 0 then return true end
	return false, errmsg
end

--touch一个文件出来，如果没有相关路径，会自动创建
function TouchFile(filePath)
	local ret, msg = Mkdir(filePath)
	if not ret then return ret, msg end
	if not posix.stat(filePath) then
		local fh = io.open(filePath, "a+")
		fh:close()
	end
end

function CopyFile(srcFile, destFile)
	os.execute("cp " .. srcFile .. " " .. destFile)
end

function MoveFile(srcFile, destFile)
	os.execute("mv " .. srcFile .. " " .. destFile)
end

function RemoveFile(file)
	os.remove(file)
end

--编译成binary的结构
local BinPath = "../binary/"
function DumpFile(srcFile, destFile)
	local s1 = posix.stat(srcFile)
	local s2 = posix.stat(destFile)
	if s1 and s2 and s2.mtime >= s1.mtime then return end
	--print("compiling ", srcFile)
	local fh = io.open(srcFile)
	local FileData = fh:read("*a")
	fh:close()

	local func, err = loadstring(FileData, srcFile)
	assert(func, err)
	if not destFile then
		destFile = BinPath..srcFile
	end
	Touch(destFile)
	local Handler = io.open(destFile,"w+")
	if not Handler then
		print("no such file", destFile)
		return
	end
	Handler:write(string.dump(func))	
	Handler:close()	
end

function DumpPath(srcPath, destPath)
	destPath = destPath or BinPath
	--print("Dumping path from "..srcPath.." to "..destPath)
	--强行清理destPath
	os.execute("rm -rf " .. destPath .. "/*")
	os.execute("mkdir -p " .. destPath)
	
	for file in posix.files(srcPath) do
		if file ~= "." and file ~= ".." and file ~=".svn" then
			local srcFile = srcPath.."/"..file
			local destFile = destPath.."/"..file
			local filetype = posix.stat(srcFile).type

			if filetype == "directory" then
				DumpPath(srcFile, destFile)

				--文件类型
			elseif filetype == "regular" then
				if string.startswith(srcFile, "./etc/") or 
					string.startswith(srcFile, "./upshell/") then
					--/etc/目录下的文件不编译
					CopyFile(srcFile, destFile)
				elseif string.endswith(srcFile, ".lua") then
					DumpFile(srcFile, destFile)
					--协议文件
				elseif string.endswith(srcFile, ".pto") then
					DumpFile(srcFile, destFile)
					--协议数据描述
				elseif string.endswith(srcFile, ".tbl") then
					DumpFile(srcFile, destFile)
				elseif string.endswith(srcFile, ".log") or 
						string.endswith(srcFile, ".cpp") or 
						string.endswith(srcFile, ".hpp") or 
						string.endswith(srcFile, ".o") then
					--nothing
				else --其他类型的文件直接copy过去
					CopyFile(srcFile, destFile)
				end
			else
				print("filetype:"..filetype..",filename:"..srcFile )
			end
		end
	end
end

-- 挖出所有扩展名为EndSwith的文件
function ListTree(path, endSwith, exclude)
	assert(path)

	local result = {}
	local excludeDirs = { ".", "..", ".svn", }
	if exclude then
		table.merge(excludeDirs, exclude)
	end
	if not string.endswith(path, "/") then
		path = path .. "/"
	end
	for fileName in posix.files(path) do
		if not table.member_key(excludeDirs, fileName) then
			local filePath = path..fileName
			local filetype = posix.stat(filePath).type
			if filetype == "directory" then
				for k, v in pairs(ListTree(filePath, endSwith, exclude)) do
					table.insert(result, v)
				end

			elseif filetype == "regular" then
				if (not endSwith) or string.endswith(filePath, endSwith) then
					if string.beginswith(filePath, "./") then
						filePath = string.sub(filePath, 3)
					end
					table.insert(result, filePath)
				end
			end
		end
	end
	return result
end

--列出指定目录下的所有文件
local IGNORE_FILE_EXTNAMES = {
	[".svn"] = 4,
	[".swp"] = 4,
	[".git"] = 4,
}

function ListFiles(path, extName, includeChildren)
	local result = {}
	local retIdx = 0
	local extLen = extName and string.len(extName)

	local function ListDir(dir)
		if not string.endswith(dir, "/") then
			dir = dir .. "/"
		end
		local stat = posix.stat(dir)
		if (not stat) or stat.type ~= "directory" then return end
		local found = false
		local filePath, strlen
		for file in posix.files(dir) do
			if file ~= "." and file ~= ".." then
				found = true
				strlen = string.len(file)
				for ext, size in ipairs(IGNORE_FILE_EXTNAMES) do
					if string.sub(file, strlen-size+1) == ext then
						found = false
						break
					end
				end
				if found then
					filePath = dir .. file
					stat = posix.stat(filePath)
					if stat.type == "regular" then
						if (not extName) or string.sub(file, strlen-extLen+1) == extName then
							retIdx = retIdx + 1
							result[retIdx] = filePath
						end
					elseif stat.type == "directory" then
						if includeChildren then
							ListDir(filePath)
						end
					end
				end
			end
		end
	end
	
	ListDir(path)
	return result
end

function ReadFile(file)
	local fh = io.open(file)
	if not fh then return nil end
	local data = fh:read("*a")
	fh:close()
	return data
end



function IsDistanceTooFar(x1, y1, x2, y2, distance)
	local xDistance = math.abs(x1 - x2)
	if xDistance > distance then return true end
	local yDistance = math.abs(y1 - y2)
	if yDistance > distance then return true end
	return xDistance * xDistance + yDistance * yDistance > distance * distance
end



-- 用时间换取空间，此函数和 os_date的性能对比：
-- 最慢的情况下，每次查询都需要重算，性能和os_date一样
-- 大部分情况下，不需要每次重算，性能是os_date的10倍左右

TIME_SLICE_SECOND	= 1
TIME_SLICE_HOUR		= 2
TIME_SLICE_DAY		= 3
TIME_SLICE_MONTH	= 4

local SliceCache = {}
function SliceTimeStr(now, sliceMode)
	now = now or os_time()
	sliceMode = sliceMode or TIME_SLICE_HOUR
	local cache = SliceCache[sliceMode]
	local reset = false
	local tag
	if (not cache) or now > cache.NextTime then
		local nextTime
		if sliceMode == TIME_SLICE_SECOND then
			tag = os_date("%Y-%m-%d %H:%M:%S", now)
			nextTime = now
		elseif sliceMode == TIME_SLICE_HOUR then
			tag = os_date("%Y%m%d%H", now)
			nextTime = now + 3600 - (now % 3600)
		elseif sliceMode == TIME_SLICE_DAY then
			tag = os_date("%Y%m%d", now)
			--获取下一天零点
			local tm = os_date("*t", now)
			tm.day = tm.day + 1
			tm.hour = 0
			tm.min = 0
			tm.sec = 0
			nextTime = os_time(tm)
		elseif sliceMode == TIME_SLICE_MONTH then
			tag = os_date("%Y%m", now)
			--获取下一月第一天的零点
			local tm = os_date("*t", now)
			tm.month = tm.month + 1
			tm.day = 1
			tm.hour = 0
			tm.min = 0
			tm.sec = 0
			nextTime = os_time(tm)
		else
			tag = os_date("%Y%m%d%H", now)
			nextTime = now + 3600 - (now % 3600)
		end
		if not cache then
			cache = {Tag = tag, NextTime = nextTime}
			SliceCache[sliceMode] = cache
		else
			cache.Tag = tag
			cache.NextTime = nextTime
		end
	end
	return cache.Tag
end

function SliceTimeYmd(now)
	return SliceTimeStr(now, TIME_SLICE_DAY)
end

function SliceTimeYmdH(now)
	return SliceTimeStr(now, TIME_SLICE_HOUR)
end

function SliceTimeYmdHMS(now)
	return SliceTimeStr(now, TIME_SLICE_SECOND)
end

function GenServerUid(HostId, SvrType, SvrIdx)
	if not HostId or (HostId == 0) then
		_RUNTIME_ERROR("[ERROR_LOGIC][GenServerUid] HostId=%s is wrong!", tostring(HostId))
		return
	end
	return (HostId*100+SvrType)*100+SvrIdx
end

function ResolveServerUid(ServerUid)
	local HostId = math.floor(ServerUid/10000)
	local ServerType = math.floor(ServerUid%10000/100)
	local SvrIdx = ServerUid%100
	return HostId, ServerType, SvrIdx
end


function PrintResult( a )
	for i = 1, #a do
		io.write(a[i], " ")
	end
	io.write("\n")
end

--生成排列组合
--取前n个元素进行排列
--例：
--a = {1,2} n = 2
--result = {1,2}, {2,1}
function Permgen( a, n )
	n = n or #a
	if n <= 1 then
		--想数组还是哈希 处理这个函数即可
		PrintResult(a)
	else
		for i = 1,n do
			a[n],a[i] = a[i],a[n]

			Permgen(a, n - 1)

			a[i],a[n] = a[n],a[i]
		end
	end
end

function os.capture(cmd, raw) 
    local f = assert(io.popen(cmd, 'r')) 
    local s = assert(f:read('*a')) 
    f:close() 
    if raw then return s end 
    s = string.gsub(s, '^%s+', '') 
    s = string.gsub(s, '%s+$', '') 
    s = string.gsub(s, '[\n\r]+', ' ') 
    return s 
end